package com.gwd.task.services.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import com.gwd.task.componts.Cache;
import com.gwd.task.entity.TestEntity;
import com.gwd.task.entity.TestProcessEntity;
import com.gwd.task.exception.DataException;
import com.gwd.task.model.*;
import com.gwd.task.repository.TestRepository;
import com.gwd.task.services.*;
import com.gwd.task.untils.ListPredicate;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zhiyongming
 * @date 2020/12/24 17:44
 */
@Service
public class TestServerImpl implements TestServer {

    @Resource
    private TestRepository testRepository;
    @Resource
    private TestProcessServer testProcessServer;
    @Resource
    private ProjectNumServer projectNumServer;
    @Resource
    private TaskServer taskServer;
    @Resource
    private ProductionEventsServer productionEventsServer;
    @Resource
    private ProjectServer projectServer;
    @Resource
    private ProjectModuleServer projectModuleServer;
    @Resource
    private AttachmentServer attachmentServer;
    @Resource
    private CodeSubmitRecordServer codeSubmitRecordServer;
    @Resource
    private CodeRepositoryServer codeRepositoryServer;
    @Resource
    private CodeRepositoryRelateServer codeRepositoryRelateServer;
    @Resource
    private Cache cache;

    //0:新建|1:已指派|2:打开|3:代码已上传|4:待测试|5:已关闭|6:再次打开|7:拒绝|8:待自测
    private final static Integer NEW = 0;
    private final static Integer DISPATCH = 1;
    private final static Integer OPEN = 2;
    private final static Integer CODE_SUBMIT = 3;
    private final static Integer PENDING_RESET = 4;
    private final static Integer CLOSE = 5;
    private final static Integer REOPEN = 6;
    private final static Integer REFUSE = 7;
    private final static Integer SELF_TEST = 8;

    private final static Integer TYPE_EVENT = 1;
    private final static Integer TYPE_TEST = 2;
    private final static Integer TYPE_RE_TEST = 3;

    private final static String NEW_INFO = "新建";
    private final static String DISPATCH_INFO = "已分配";
    private final static String OPEN_INFO = "打开";
    private final static String CODE_SUBMIT_INFO = "代码已提交";
    private final static String PENDING_RESET_INFO = "待测试";
    private final static String CLOSE_INFO = "已关闭";
    private final static String REOPEN_INFO = "再次打开";
    private final static String REFUSE_INFO = "拒绝";
    private final static String SELF_TEST_INFO = "待自测";

    @Override
    @SuppressWarnings("all")
    public PageUtils findByPage(TestDTO dto, PageUtils pageUtils) {

        Page<TestEntity> page = testRepository.findAll((Specification<TestEntity>)
                        (root, query, cb) ->
                                ListPredicate.of(root, query, cb)
                                        .equal("projectId", dto.getProjectId())
                                        .equal("groupId", dto.getGroupId()) //组别
                                        .equal("lv2ModuleId", dto.getLv2ModuleId())
                                        .equal("devPerson", dto.getDevPerson())
                                        .equal("testPerson", dto.getTestPerson())
                                        .equal("type", dto.getType())
                                        .equal("status", dto.getStatus())
                                        .notEqual("status", dto.getNoClose()?5:null) //是否关闭是否为真，如果是则状态不等于5
                                        .notEqual("status", dto.getNoRefuse()?7:null) //是否关闭是否为真，如果是则状态不等于5
                                        .finish()
                                        .getRestriction(),
                pageUtils.convert("tId", "desc"));

        List<TestDTO> list = page.get().map(TestDTO::new).collect(Collectors.toList());

        //匹配项目名称
        List<ProjectDTO> projectDTOList = projectServer.findAll();
        list.forEach(test -> projectDTOList.forEach(project -> {
            if (test.getProjectId().equals(project.getProjectId())) {
                test.setProjectName(project.getProjectName());
            }
        }));

        //匹配模块名称
        List<ProjectModuleDTO> projectModuleDTOS = projectModuleServer.findAll();
        list.forEach(test -> projectModuleDTOS.forEach(module -> {
            if (test.getLv1ModuleId().equals(module.getModuleId())) {
                test.setLv1ModuleName(module.getModuleName());
            }
            if (test.getLv2ModuleId().equals(module.getModuleId())) {
                test.setLv2ModuleName(module.getModuleName());
            }
        }));

        //匹配附件
        List<AttachmentDTO> attachments = attachmentServer.findByCodeList(list.stream().map(TestDTO::getTestCode).collect(Collectors.toList()));
        list.forEach(test -> attachments.forEach(attach -> {
            if (test.getTestCode().equals(attach.getCode())) {
                test.addAttachment(attach);
            }
        }));

        //匹配仓库
        List<CodeRepositoryDTO> codeRepository = codeRepositoryServer.findAll();
        try {
            list.forEach(test -> codeRepository.forEach(repos -> {
                if (test.getCrId().equals(repos.getCrId())) {
                    test.setCodeRepositoryName(repos.getType());
                }
            }));
        } catch (NullPointerException e) {

        }

        //匹配任务名称
        List<TaskDTO> taskList = taskServer.findAllById(list.stream().map(TestDTO::getTaskId).collect(Collectors.toList()));
        taskList.forEach(task -> {
            list.forEach(test -> {
                if (test.getTaskId().equals(task.getTaskId())) {
                    test.setTaskName(task.getDecomposition());
                    test.setTask_describe(task.getDescribe());
                    test.setTask_decomposition(task.getDecomposition());
                }
            });
        });

        //匹配用户名
        cache.matchUserName(list, "getRespPerson", "setRespPersonName");
        cache.matchUserName(list, "getDevPerson", "setDevPersonName");
        cache.matchUserName(list, "getTestPerson", "setTestPersonName");

        pageUtils.convert(page).setContent(list);
        return pageUtils;
    }

    @Override
    @SuppressWarnings("unchecked")
    public HSSFWorkbook export(TestDTO dto)  {
        PageUtils pageUtils = new PageUtils();
        pageUtils.setPageNum(1);
        pageUtils.setPageSize(100000000);
        List<?> content = findByPage(dto, pageUtils).getContent();
        if (content.isEmpty()) {
            return null;
        }
        List<TestDTO> list = (List<TestDTO>) content;
        HSSFWorkbook workbook = new HSSFWorkbook();
        HSSFCellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.index);
        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setAlignment(HorizontalAlignment.CENTER_SELECTION);

        HSSFCellStyle leftCellStyle = workbook.createCellStyle();
        leftCellStyle.setAlignment(HorizontalAlignment.LEFT);
        leftCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

        HSSFCellStyle centerCellStyle = workbook.createCellStyle();
        centerCellStyle.setAlignment(HorizontalAlignment.CENTER_SELECTION);
        centerCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

        HSSFSheet sheet = workbook.createSheet();
        HSSFRow row = sheet.createRow(0);


        HSSFCell hssfCell0 = row.createCell(0);
        HSSFCell hssfCell1 = row.createCell(1);
        HSSFCell hssfCell2 = row.createCell(2);
        HSSFCell hssfCell3 = row.createCell(3);
        HSSFCell hssfCell4 = row.createCell(4);
        HSSFCell hssfCell5 = row.createCell(5);
        HSSFCell hssfCell6 = row.createCell(6);
        HSSFCell hssfCell7 = row.createCell(7);
        HSSFCell hssfCell8 = row.createCell(8);
        HSSFCell hssfCell9 = row.createCell(9);
        HSSFCell hssfCell10 = row.createCell(10);
        HSSFCell hssfCell11 = row.createCell(11);

        hssfCell0.setCellValue("状态");
        hssfCell1.setCellValue("编号");
        hssfCell2.setCellValue("类型");
        hssfCell3.setCellValue("发现时间");
        hssfCell4.setCellValue("项目");
        hssfCell5.setCellValue("模块");
        hssfCell6.setCellValue("子模块");
        hssfCell7.setCellValue("开发负责人");
        hssfCell8.setCellValue("测试负责人");
        hssfCell9.setCellValue("预计修复时间");
        hssfCell10.setCellValue("预计上线时间");
        hssfCell11.setCellValue("问题描述");

        hssfCell0.setCellStyle(cellStyle);
        hssfCell1.setCellStyle(cellStyle);
        hssfCell2.setCellStyle(cellStyle);
        hssfCell3.setCellStyle(cellStyle);
        hssfCell4.setCellStyle(cellStyle);
        hssfCell5.setCellStyle(cellStyle);
        hssfCell6.setCellStyle(cellStyle);
        hssfCell7.setCellStyle(cellStyle);
        hssfCell8.setCellStyle(cellStyle);
        hssfCell9.setCellStyle(cellStyle);
        hssfCell10.setCellStyle(cellStyle);
        hssfCell11.setCellStyle(cellStyle);

        for (int i=0; i<list.size(); i++) {
            HSSFRow rowI = sheet.createRow(i+1);
            rowI.createCell(0).setCellValue(list.get(i).getStatusName());
            rowI.createCell(1).setCellValue(list.get(i).getTestCode());
            rowI.createCell(2).setCellValue(list.get(i).getTypeName());
            rowI.createCell(3).setCellValue(list.get(i).getFindTime());
            rowI.createCell(4).setCellValue(list.get(i).getProjectName());
            rowI.createCell(5).setCellValue(list.get(i).getLv1ModuleName());
            rowI.createCell(6).setCellValue(list.get(i).getLv2ModuleName());
            rowI.createCell(7).setCellValue(list.get(i).getDevPersonName());
            rowI.createCell(8).setCellValue(list.get(i).getTestPersonName());
            rowI.createCell(9).setCellValue(list.get(i).getPreRepair());
            rowI.createCell(10).setCellValue(list.get(i).getPreOnline());
            rowI.createCell(11).setCellValue(list.get(i).getDescribe());
        }


        sheet.setColumnWidth(0, (int)35.7*80);
        sheet.setColumnWidth(1, (int)35.7*100);
        sheet.setColumnWidth(3, (int)35.7*200);
        sheet.setColumnWidth(4, (int)35.7*100);
        sheet.setColumnWidth(5, (int)35.7*100);
        sheet.setColumnWidth(6, (int)35.7*100);
        sheet.setColumnWidth(7, (int)35.7*100);
        sheet.setColumnWidth(8, (int)35.7*100);
        sheet.setColumnWidth(9, (int)35.7*100);
        sheet.setColumnWidth(10, (int)35.7*100);
        sheet.setColumnWidth(11, (int)35.7*100);
        workbook.setSheetName(0,"sheet1");

        FileOutputStream out;
        String fileName = UUID.randomUUID().toString().replace("-", "");
        try {
            out = new FileOutputStream("E:\\files\\" + fileName);
            workbook.write(out);
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return workbook;
    }

    @Override
    public void save(TestDTO dto, Long userId) {
        TestEntity save;
        if (dto.getTest_id()!=null) {
            Optional<TestEntity> entity = testRepository.findById(dto.getTest_id());
            if (!entity.isPresent()) {
                throw new DataException("test id:" + dto.getTest_id() + " 对应的数据不存在.");
            }
            save = entity.get();
        } else {
            String code = projectNumServer.getTest(dto.getProjectId());
            save = new TestEntity();
            save.setTestCode(code);
            save.setStatus(NEW);
            save.setType(dto.getIsBack()==1?3:2); //是否回归测试
            save.setFindTime(DatePattern.NORM_DATETIME_MINUTE_FORMAT.format(new Date()));
        }


        List<CodeRepositoryDTO> codeRepository = codeRepositoryServer.findAll();
        List<CodeSubmitRelateDTO> relates = codeRepositoryRelateServer.findByProject(dto.getProjectId());
        relates.forEach(r -> codeRepository.forEach(c -> {
            if (c.getType().equals(dto.getCodeRepositoryName())&&c.getCrId().equals(r.getCrId())) {
                save.setCrId(c.getCrId());
            }
        }));

        BeanUtil.copyProperties(dto, save, CopyOptions.create().setIgnoreNullValue(true).setIgnoreError(true));
        TestEntity entity = testRepository.save(save);

        attachmentServer.bindCode(dto.getAttachments(), entity.getTestCode());

        if (dto.getTId()==null) {
            testProcessServer.save(TestProcessDTO.LOG(userId, entity.getTId(), NEW, NEW_INFO));
        }
    }

    @Override
    public void self(TestDTO test, Long userId) {
        /*
         * 1.获取图片临时编号
         * 2.更新attachment对应的编号与类型
         * 3.更新测试状态
         * 4.更新日志表
         */
        testRepository.findById(test.getTId()).ifPresent(entity -> attachmentServer.self(test.getTempCode(), entity.getTestCode()));
        reset(test.getTId(), userId);

    }

    @Override
    public void board(List<Long> taskIds, List<Long> eventsIds, List<Long> testIds, Long crId) {
        if (!testIds.isEmpty()) {
            testRepository.batchPendingRest(testIds);
        }
        if (!taskIds.isEmpty()) {
            List<TaskDTO> tasks = taskServer.findAllById(taskIds);
            batchRecordLog(testRepository.saveAll(convertByTask(tasks, crId)));
        }
        if (!eventsIds.isEmpty()) {
            List<ProductionEventsDTO> events = productionEventsServer.findAllById(eventsIds);
            batchRecordLog(testRepository.saveAll(convertByEvent(events, crId)));
        }
    }

    @Override
    public List<TestDTO> findByUserIdListAndUnClose(List<Long> ids) {
        //状态为 2 3 4 6 7 8
        List<Integer> status = new ArrayList<>();
        status.add(DISPATCH);
        status.add(CODE_SUBMIT);
        status.add(PENDING_RESET);
        status.add(REOPEN);
        status.add(SELF_TEST);
        //类型为2 3 测试 回归测试
        List<Integer> type = new ArrayList<>();
        type.add(TYPE_EVENT);
        type.add(TYPE_TEST);
        type.add(TYPE_RE_TEST);
        return testRepository.findByDevPersonInAndStatusInAndTypeIn(ids, status, type).stream().map(TestDTO::new).collect(Collectors.toList());
    }

    //批量记录日志
    private void batchRecordLog(List<TestEntity> list) {
        testProcessServer.saveAll(
                list.stream().map(test -> new TestProcessEntity(test, SELF_TEST, SELF_TEST_INFO)).collect(Collectors.toList())
        );
    }

    private List<TestEntity> convertByTask(List<TaskDTO> tasks, Long crId) {
        List<TestEntity> list = new ArrayList<>();
        tasks.forEach(task -> {
            TestEntity entity = new TestEntity();
            entity.setRelateId(task.getTaskId());
            entity.setTestCode(task.getTaskCode());
            entity.setStatus(SELF_TEST);
            entity.setGroupId(task.getGroup());
            entity.setProjectId(task.getProjectId());
            entity.setLv1ModuleId(task.getLv1ModuleId());
            entity.setLv2ModuleId(task.getLv2ModuleId());
            entity.setTestPerson(task.getTestPerson());
            entity.setDevPerson(task.getDevPerson());
            entity.setRespPerson(task.getDevPerson());
            entity.setTaskId(task.getTaskId());
            entity.setCrId(crId); //仓库id
            entity.setType(0);
            entity.setDescribe(task.getDescribe());
            entity.setFindTime(DatePattern.NORM_DATETIME_MINUTE_FORMAT.format(new Date()));

            list.add(entity);
        });
        return list;
    }

    private List<TestEntity> convertByEvent(List<ProductionEventsDTO> events, Long crId) {
        List<TestEntity> list = new ArrayList<>();
        events.forEach(event -> {
            TestEntity entity = new TestEntity();
            entity.setRelateId(event.getPeId());
            entity.setTestCode(event.getPeCode());
            entity.setStatus(SELF_TEST);
            entity.setGroupId(Long.valueOf(event.getGroup()));
            entity.setProjectId(event.getProjectId());
            entity.setLv1ModuleId(event.getLv1ModuleId());
            entity.setLv2ModuleId(event.getLv2ModuleId());
            entity.setTestPerson(event.getTestPerson());
            entity.setDevPerson(event.getDevPerson());
            entity.setRespPerson(event.getRespPerson());
            entity.setType(1);
            entity.setCrId(crId); //仓库id
            entity.setDescribe(event.getDescribe());
            entity.setQuestionType(event.getPeType());
            entity.setTaskId(event.getPeId());
            entity.setFindTime(new SimpleDateFormat("yyyy-MM-dd").format(event.getRecord()));

            list.add(entity);
        });
        return list;
    }

    @Override
    public void dispatch(Long testId, Long userId) {
        testRepository.findById(testId).ifPresent(test -> {
            test.setStatus(DISPATCH);
            testRepository.save(test);

            testProcessServer.save(TestProcessDTO.LOG(userId, testId, DISPATCH, DISPATCH_INFO));
        });
    }

    @Override
    public void open(Long testId, Long userId) {
        testRepository.findById(testId).ifPresent(test -> {
            test.setStatus(OPEN);
            testRepository.save(test);

            testProcessServer.save(TestProcessDTO.LOG(userId, testId, OPEN, OPEN_INFO));
        });
    }

    @Override
    public void submit(Long testId, Long userId, CodeSubmitRecordDTO csrDTO) {
        testRepository.findById(testId).ifPresent(test -> {
            test.setStatus(CODE_SUBMIT);
            testRepository.save(test);

            testProcessServer.save(TestProcessDTO.LOG(userId, testId, CODE_SUBMIT, CODE_SUBMIT_INFO));

            //保存至提交记录中
            csrDTO.setCode(test.getTestCode());
            csrDTO.setSubmitUser(userId);
            csrDTO.setSubmitTime(DateTime.now().toStringDefaultTimeZone());
            csrDTO.setProjectId(test.getProjectId());
            csrDTO.setLv1ModuleId(test.getLv1ModuleId());
            csrDTO.setLv2ModuleId(test.getLv2ModuleId());
            csrDTO.setDescribe(test.getDescribe());
            csrDTO.setRelateId(test.getTId());
            csrDTO.setType(2); //测试

            codeSubmitRecordServer.save(csrDTO);
        });
    }

    @Override
    public TestEntity close(Long testId, Long userId) {
        Optional<TestEntity> optional = testRepository.findById(testId);
        optional.ifPresent(test -> {
            test.setStatus(CLOSE);
            testRepository.save(test);

            testProcessServer.save(TestProcessDTO.LOG(userId, testId, CLOSE, CLOSE_INFO));
            //0:任务、1:生产
            Integer type = test.getType();
            if (testRepository.findByOther(test.getRelateId(), testId, CLOSE).size()==0) {
                if (type == 0) {
                    taskServer.finish(test.getTaskId(), userId);
                }
                if (type == 1) {
                    productionEventsServer.finish(test.getRelateId(), userId);
                }
            }
        });
        return optional.orElse(null);
    }

    @Override
    public void close2(TestDTO testDTO, Long userId) {
        TestEntity entity = close(testDTO.getTId(), userId);
        if (entity!=null) {
            attachmentServer.bindCode(testDTO.getTempCode(), entity.getTestCode());
        }
    }

    @Override
    public void reOpen(Long testId, Long userId) {
        testRepository.findById(testId).ifPresent(test -> {
            test.setStatus(OPEN);
            testRepository.save(test);

            testProcessServer.save(TestProcessDTO.LOG(userId, testId, REOPEN, REOPEN_INFO));
        });
    }

    @Override
    public void refuse(Long testId, Long userId) {
        testRepository.findById(testId).ifPresent(test -> {
            test.setStatus(REFUSE);
            testRepository.save(test);

            testProcessServer.save(TestProcessDTO.LOG(userId, testId, REFUSE, REFUSE_INFO));
        });
    }

    @Override
    public void reset(Long testId, Long userId) {
        testRepository.findById(testId).ifPresent(test -> {
            test.setStatus(PENDING_RESET);
            testRepository.save(test);

            testProcessServer.save(TestProcessDTO.LOG(userId, testId, PENDING_RESET, PENDING_RESET_INFO));
        });
    }
}
